package utils;

import java.lang.annotation.Annotation;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.util.Arrays;
import java.util.List;
import java.util.stream.Collectors;

public class ReflectionUtil {


    /**
     * 获取类中所有属性
     * @param obj 类
     * @return {@link Field[]}
     */
    public static Field[] findAllField(Class<?> obj){
        Field[] fields = obj.getDeclaredFields();
        Arrays.stream(fields).forEach(e->e.setAccessible(true));
        return fields;
    }

    /**
     *  获取当前类所有方法
     * @param obj 类
     * @return {@link Method[]}
     */
    public static Method[] findAllMethods(Class<?> obj){
        Method[] declaredMethods = obj.getDeclaredMethods();
        for (Method declaredMethod : declaredMethods) {
            declaredMethod.setAccessible(true);
        }
        return declaredMethods;
    }


    /**
     * 获取方法名称
     * @param obj entity
     * @return List 方法名称
     */
    public static List<String> findAllMethodsName(Class<?> obj){
        Method[] allMethods = findAllMethods(obj);
        return Arrays.stream(allMethods).map(Method::getName).collect(Collectors.toList());
    }

    /**
     * 判断某个类 某个方法上是否含有某注解
     * @param annoClass 注解类型
     * @return boolean
     */
    public static boolean hasAnnotation(Method method,Class<? extends Annotation> annoClass){
        return method != null && method.isAnnotationPresent(annoClass);
    }

    /**
     * 判断方法上是否含有某注解
     * @param obj 类
     * @param method 方法
     * @param annoClass 注解类型
     * @return boolean
     */
    public static boolean hasAnnotation(Class<?> obj,Method method,Class<? extends Annotation> annoClass){
        Method targetMethod = findMethodByNameAndParamNum(obj, method,method.getParameterTypes());
        if (targetMethod!=null){
            return method.isAnnotationPresent(annoClass);
        }
        return false;

    }

    /**
     * obj找方法 到不到返回null
     *
     * @param methodName 方法名称
     * @param paramNum   参数个数
     * @param obj        对象
     * @param returnType 返回值类型
     * @param paramsType 参数类型,按顺序填写
     * @return {@link Method}
     */
    public static Method findMethodByNameAndParamNum(Class<?> obj,Class<?> returnType,String methodName,Integer paramNum,Class<?>...paramsType){
        Method[] allMethods = findAllMethods(obj);
        //方法名称相同,参数数量相同,返回值类型相同

        List<Method> methodList = Arrays.stream(
                allMethods).filter(e -> e.getName().equals(methodName) && e.getParameterCount()==paramNum && e.getReturnType().equals(returnType))
                .collect(Collectors.toList());
        if (methodList.isEmpty()){
            return null;
        }
        //方法名对,参数个数也对,返回值类型,且只有一个
        if (methodList.size()==1){
            return methodList.get(0);
        }
        //核对的方法类型相同个数
        int foundSize;
        //目标参数类型相同个数
        int targetFoundSize;
        //该方法在方法列表里的下标
        int mIndex;
        //比较参数类型
        for (int k = 0; k < methodList.size(); k++) {
            //遍历每一个方法
            foundSize=0;
            Class<?>[] parameterTypes = methodList.get(k).getParameterTypes();
            targetFoundSize=parameterTypes.length;
            mIndex=k;
            if (paramsType.length>0){
                if (parameterTypes.length>0){
                    for (int i = 0; i < parameterTypes.length; i++) {

                        if (parameterTypes[i].equals(paramsType[i])){
                            //保存下标
                            foundSize++;
                        }
                    }
                }
            }
            //说明方法类型也相同
            if (foundSize==targetFoundSize){
                return methodList.get(mIndex);
            }
        }
        return null;
    }


    /**
     * obj 找方法
     * @param obj 对象
     * @param mod 方法
     * @return {@link Method}
     */
    public static Method findMethodByNameAndParamNum(Class<?> obj,Method mod,Class<?>...paramsType){
        Method[] allMethods = findAllMethods(obj);
        //方法名称相同,参数数量相同,返回值类型相同
        List<Method> methodList = Arrays.stream(
                        allMethods).filter(e -> e.getName().equals(mod.getName()) && e.getParameterCount()==mod.getParameterCount() && e.getReturnType().equals(mod.getReturnType()))
                .collect(Collectors.toList());
        if (methodList.isEmpty()){
            return null;
        }
        //方法名对,参数个数也对,返回值类型,且只有一个
        if (methodList.size()==1){
            return methodList.get(0);
        }
        //核对的方法类型相同个数
        int foundSize=0;
        //目标参数类型相同个数
        int targetFoundSize = 0;
        //该方法在方法列表里的下标
        int mIndex = 0;
        //比较参数类型
        for (Method method : methodList) {
            //遍历每一个方法
            foundSize=0;
            Class<?>[] parameterTypes = method.getParameterTypes();
            targetFoundSize=parameterTypes.length;
            if (paramsType.length>=1){
                if (parameterTypes.length>=1){
                    for (int i = 0; i < parameterTypes.length; i++) {
                        if (parameterTypes[i].equals(paramsType[i])){
                            //保存下标
                            mIndex=i;
                            foundSize++;
                        }
                    }
                }
            }
        }
        //说明方法类型也相同
        if (foundSize==targetFoundSize){
            return methodList.get(mIndex);
        }
        return null;
    }
}
